#include <fhe/core/XmlEncoder.h>
#include <fhe/core/Util.h>
#include <fhe/core/Registerers.h>
#include <tinyxml.h>

namespace fhe
{
  namespace core
  {
    FHE_ENCODER( XmlEncoder );
    
    std::string XmlEncoder::extension() const
    {
      return ".xml";
    }
    
    namespace 
    {
      void saveImpl( const Data::Ptr& data, TiXmlElement* elem )
      {
        switch ( data->getType() )
        {
          case Data::PARENT:
          {
            for ( Data::ChildIterator child = data->childrenBegin(); 
                  child != data->childrenEnd(); ++child )
            {
              TiXmlElement* childElem = new TiXmlElement( (*child)->getName() );
              elem->LinkEndChild( childElem );
              saveImpl( *child, childElem );
            }
            break;
          }
          case Data::BOOL:
          {
            elem->LinkEndChild( new TiXmlText( data->getBool() ? "true" : "false" ) );
            break;
          }
          case Data::INT:
          {
            std::ostringstream os;
            os << data->getInt();
            elem->LinkEndChild( new TiXmlText( os.str() ) );
            break;
          }
          case Data::DOUBLE:
          {
            std::ostringstream os;
            os << data->getDouble();
            elem->LinkEndChild( new TiXmlText( os.str() ) );
            break;
          }
          case Data::STRING:
          {
            elem->LinkEndChild( new TiXmlText( data->getString() ) );
            break;
          }
          default:
          {
            FHE_ERROR( "unknown data type %d in data %s", data->getType(), data->path().c_str() );
            break;
          }
        }
      }
      
      Data::Ptr loadImpl( TiXmlElement* elem )
      {
        Data::Ptr data( new Data );
        data->setName( elem->Value() );
        
        if ( elem->GetText() || ( !elem->FirstAttribute() && !elem->FirstChildElement() ) )
        {
          data->setValueString( elem->GetText() ? elem->GetText() : std::string() );
        }
        else
        {
          for ( TiXmlAttribute* attr = elem->FirstAttribute(); attr; attr = attr->Next() )
          {
            Data::Ptr child( new Data );
            child->setName( attr->Name() );
            child->setValueString( attr->ValueStr() );
            data->addChild( child );
          }
          
          for ( TiXmlElement* childElem = elem->FirstChildElement(); 
               childElem; childElem = childElem->NextSiblingElement() )
          {
            data->addChild( loadImpl( childElem ) );
          }
        }
        
        return data;
      }
    }
    
    void XmlEncoder::save( const Data::Ptr& data, const std::string& filename ) const
    {
      TiXmlDocument doc;
      TiXmlElement* elem = new TiXmlElement( data->getName() );
      doc.LinkEndChild( elem );
      saveImpl( data, elem );
      doc.SaveFile( filename );
    }
    
    Data::Ptr XmlEncoder::load( const std::string& filename ) const
    {
      TiXmlDocument doc;
      FHE_ASSERT_MSG( doc.LoadFile( filename.c_str() ), 
                      "failed to load %s: %s", 
                      filename.c_str(), doc.ErrorDesc() );
      return loadImpl( doc.RootElement() );
    }
  }
}
